/*
 * $Id$
 */
package corejs.rt;

import static corejs.rt.Undefined.UNDEFINED;

import java.math.BigDecimal;

import java.util.Map;

public abstract class Number extends Primitive {

   // Number.prototype
   public static final Value prototype = new Obj(Obj.prototype); 
   
   static void _number_init_prototype_ () {
      prototype.store("toString", new Invocable() {
         @Override
         public Str invoke(final ExecutionContext ec) { 
            final Value v = ec.get();
            return (v.isPrimitive() ? v.cast(Primitive.class)
                                    : v.cast(Obj.class).toPrimitive(ec)).toStr();
         }
      });
       
      prototype.store("valueOf", new Invocable() {
         @Override
         public Value invoke(final ExecutionContext ec) {
            return ec.get();
         }
      });
   }
   
   public Number() {
      super(prototype);
   }
   
   public static final Number NaN = new Number() {
      
      @Override public Number add(final Number n) {return this;}
      @Override protected Number add(final BigDecimal d) {return this;}
      @Override protected Number add(final int i) {return this;}
      @Override protected Number add(final long l) {return this;}

      @Override public Number multiply(final Number other) {return this;}
      @Override protected Number multiply(final int i) { return this; }
      @Override protected Number multiply(final long l) { return this; }
      @Override protected Number multiply(final BigDecimal d) { return this; }

      @Override public Number subtract(final Number other) {return this;}
      @Override protected Number subtract(final int i) { return this; }
      @Override protected Number subtract(final long l) { return this; }
      @Override protected Number subtract(final BigDecimal d) { return this; }

      @Override public Bool toBool() {return Bool.FALSE;}
      
      @Override public Str toStr() {return Str.NaN;}
      
      @Override public Number compareTo(final Number other) {return this;}
      @Override Number compareTo(final int i) {return this;}
      @Override Number compareTo(final long l) {return this;}
      @Override Number compareTo(final BigDecimal d) {return this;}
      
      @Override 
      public boolean equals(final Number other) { return false; }

      @Override
      public boolean equalsStrictly(final Value other) { return false; }

      @Override
      public boolean equals(final Value y, final ExecutionContext ec) {
         return false;
      }


      @Override public boolean equals(final BigDecimal d) {return false;}
      @Override public boolean equals(final long l) {return false;}
      @Override public boolean equals(final int i) {return false;}
      
      @Override public long longValue() {return 0;}
      @Override public int intValue() {return 0;}
      
      @Override public Number neg() {return this;}
   };

   /**
    *
    * @return 
    */
   @Override
   public Number toNumber() {return this;}

    /**
    * 
    * @param n
    * @return 
    */
   public abstract Number add(final Number n);
  
   /**
    * 
    * @param i
    * @return 
    */   
   protected abstract Number add(final int i);

   /**
    * 
    * @param d
    * @return 
    */
   protected abstract Number add(final BigDecimal d);

   /**
    * 
    * @param l
    * @return 
    */
   protected abstract Number add(final long l);

   /**
    * 
    * @param s
    * @return 
    */
   public Str multiply(final Str s) {
      final StringBuilder sb = new StringBuilder();
      for (int i = 0; i < longValue(); ++i) {
         sb.append(s.toString());
      }
      return new Str(sb);
   }
   
   /**
    * 
    * @param n
    * @return 
    */
   public abstract Number multiply(final Number n);

   /**
    * 
    * @param i
    * @return 
    */
   protected abstract Number multiply(final int i);

   /**
    * 
    * @param l
    * @return 
    */
   protected abstract Number multiply(final long l);


   /**
    * 
    * @param d
    * @return 
    */
   protected abstract Number multiply(final BigDecimal d);

   /**
    * 
    * @param n
    * @return 
    */
   public abstract Number subtract(final Number n);

  /**
    * 
    * @param i
    * @return 
    */
   protected abstract Number subtract(final int i);

   /**
    * 
    * @param l
    * @return 
    */
   protected abstract Number subtract(final long l);

   /**
    * 
    * @param d
    * @return 
    */
   protected abstract Number subtract(final BigDecimal d);
   
   /**
    *
    * @param i
    * @return
    */
   public abstract boolean equals(final int i);

   /**
    *
    * @param l
    * @return
    */
   public abstract boolean equals(final long l);

   /**
    *
    * @param d
    * @return
    */
   public abstract boolean equals(final BigDecimal d);

   /**
    * 
    * @param other
    * @return 
    */
   public abstract Number compareTo(final Number other);

   /**
    * 
    * @param i
    * @return 
    */
   abstract Number compareTo(final int i);

   /**
    * 
    * @param l
    * @return 
    */
   abstract Number compareTo(final long l);

   /**
    * 
    * @param d
    * @return 
    */
   abstract Number compareTo(final BigDecimal d);
   
   /**
    *
    * @return
    */
   abstract public long longValue();

   /**
    * 
    * @return 
    */
   abstract public int intValue();
   
   /**
    * 
    */
   abstract public Number neg();
   
   /**
    *
    * @return
    */
   @Override public Str typeof() {return Str.NUMBER;}


   @Override
   public boolean equals(final Primitive other) {
      return this == other || equals(other.toNumber());
   }
   
   public abstract boolean equals(final Number n);

   @Override
   public boolean equalsStrictly(final Value y) {
      return this == y || (y.isNumber() && equals(y.cast(Number.class)));
   }

}
